﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Reflection;
using System.Globalization;

using System.Web.Configuration;

namespace DiskFileUpload
{
    public class UploadModule : System.Web.IHttpModule
    {
        public static void Register()
        {
            DynamicHttpModuleManager.RegisterDynamicModule
                (
                    () => new UploadModule()
                );
        }

        public void Dispose() { }

        public void Init(HttpApplication application)
        {
            application.BeginRequest += new EventHandler(Application_BeginRequest);
        }

        public void Application_BeginRequest(object sender, EventArgs e)
        {
            HttpContext context = HttpContext.Current;
            HttpRequest request = context.Request;

            string contentType = request.ContentType.ToLower();
            // 当前请求参数的类型
            if (!contentType.StartsWith("multipart/form-data"))
                return;

            // HttpContext 显式实现的一个接口
            IServiceProvider provider = HttpContext.Current;
            HttpWorkerRequest worker = (HttpWorkerRequest)provider.GetService(typeof(HttpWorkerRequest));

            // 可能是上传文件，判断是否有内容
            // 如果没有内容，直接返回
            if (!worker.HasEntityBody())
                return; ;

            // 请求的头部一定已经读取了
            // 读取边界字符串
            string boundary = contentType.Substring(contentType.IndexOf("boundary=") + 9);

            // 包装 Worker 对象, IFileUpload 用于测试方便
            DiskFileUpload.IFileUpload fu
                = new DiskFileUpload.HttpWorkderFileUpload(
                    worker,
                    request.ContentEncoding
                    );

            DiskFileUpload.MultipartStream stream
                    = new DiskFileUpload.MultipartStream(
                        fu,
                        boundary,
                        8192
                        );

            // 用于保存请求参数的集合
            DiskFileUpload.UploadFiles list
                = new DiskFileUpload.UploadFiles();

            fu.ReadComplete += list.CompletePercentHander;

            bool nextSection = stream.SkipPreamble();
            while (nextSection)
            {
                DiskFileUpload.FileItem item = stream.ReadHeader();

                stream.ReadBodyData(item);
                item.Finish();

                if (item.Name == UploadFiles.DiskFileUpload_GUID)
                {
                    string uploadId = item.Value;
                    list.ApplicationKey = uploadId;
                    context.Application[uploadId] = list;
                }
                list.Add(item);


                nextSection = stream.ReadBoundary();
            }

            // 全部读取完成后，需要重新设置请求参数，以便 ASP.NET 继续处理
            BindingFlags flags1 = BindingFlags.NonPublic | BindingFlags.Instance;
            Type type1 = request.GetType();
            FieldInfo info1 = type1.GetField("_rawContent", flags1);
            FieldInfo info2 = type1.GetField("_contentLength", flags1);

            // string text = "-----------------------------7da287204c096e\r\nContent-Disposition: form-data; name=\"__VIEWSTATE\"\r\n\r\n/wEPDwUKMTA0NTIwNTUzOA9kFgICAw8WAh4HZW5jdHlwZQUTbXVsdGlwYXJ0L2Zvcm0tZGF0YWRkmrn1XpupmgKCCfQdgqHzbDhhOC++01j3LRcS0VNguQA=\r\n-----------------------------7da287204c096e\r\nContent-Disposition: form-data; name=\"__EVENTVALIDATION\"\r\n\r\n/wEWAgL0yoPbCQKM54rGBgTdg3lAdKlNc2WCnKzxWPP89si7RmIJ2lYTaqXKCUGx\r\n-----------------------------7da287204c096e\r\nContent-Disposition: form-data; name=\"FileUpload1\"\r\n\r\nContent-Type: image/pjpeg; filename=\"1600-1200.jpg\"; filePath=\"9a5f3c3b-7981-43d1-bcfc-04e4be54fb57.jpg\"\r\n";

            string text = list.ToContentString(boundary);
            byte[] textParts = request.ContentEncoding.GetBytes(text);

            if ((info1 != null) && (info2 != null))
            {
                // 
                Assembly web = Assembly.GetAssembly(typeof(HttpRequest));
                Type hraw = web.GetType("System.Web.HttpRawUploadedContent");

                // 构造函数的参数
                // 1. 保存到文件中时，最低的门槛尺寸
                // 2. 内容的长度
                object[] argList = new object[2];
                argList[0] = textParts.Length;
                argList[1] = textParts.Length;

                // 通过创建 HttpRawUploadedContent 对象实例
                CultureInfo currCulture = CultureInfo.CurrentCulture;
                object httpRawUploadedContent = Activator.CreateInstance(
                    hraw,
                    BindingFlags.NonPublic | BindingFlags.Instance,
                    null,
                    argList,
                    currCulture,
                    null);

                Type uploadContentType = httpRawUploadedContent.GetType();

                // 设置 HttpRawUploadedContent 内容的属性
                FieldInfo dataField = uploadContentType.GetField("_data", flags1);
                dataField.SetValue(httpRawUploadedContent, textParts);

                FieldInfo lengthField = uploadContentType.GetField("_length", flags1);
                lengthField.SetValue(httpRawUploadedContent, textParts.Length);

                // 必须设置 _completed 属性
                FieldInfo fileThresholdField = uploadContentType.GetField("_completed", flags1);
                fileThresholdField.SetValue(httpRawUploadedContent, true);

                // 替换掉 HttpRequest 的私有成员
                info1.SetValue(request, httpRawUploadedContent);
                info2.SetValue(request, textParts.Length);
            }
        }
    }
}